/* eslint-disable jsdoc/require-property-description */
/* eslint-disable jsdoc/no-restricted-syntax */
// @module es2020
import { isPromise } from "node:util/types";
import Q from "q";

/**
 * @interface Pro
 * @property {string} status
 * @property {any} reason
 */
enhanceQ.$inject = ["$q"];

/**
 * @param {ng.IQEnhanceService} $delegate
 * @returns {ng.IQEnhanceService}
 */
function enhanceQ($delegate) {
  // 返回第一个成功的
  $delegate.allSettled = allSettled;

  // 返回任意一个成功的 TODO:
  // $delegate.any = any;

  $delegate.isNgIPromise = isNgIPromise;

  // $delegate.any = function any(promises){
  // if (!promises.length) {
  //       throw new AggregateError("All promises were rejected");
  //     }
  // };

  return $delegate;

  /**
   * @template {unknown[] | []} T
   * @param {T} promises
   * @returns {ng.IPromise<{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>>; }>}
   */
  function allSettled(promises) {
    if (!promises.length) {
      return $delegate.resolve(
        // eslint-disable-next-line no-warning-comments
        // @ts-ignore
        /** @type {{ -readonly [P in keyof T]: PromiseSettledResult<Awaited<T[P]>>; }} */ ({}),
      );
    }
    const res = [];
    const defer = $delegate.defer();
    // q.isPromise(promises);
    $delegate
      .all(
        promises.map((promiseItem) => {
          if (isNgIPromise(promiseItem)) {
            return promiseItem.then(
              (value) =>
                /** @type {PromiseFulfilledResult<T>} */ ({
                  status: "fulfilled",
                  value,
                }),
              (err) =>
                /** @type {PromiseRejectedResult} */ ({
                  status: "rejected",
                  reason: err,
                }),
            );
          }
          return /** @type {PromiseFulfilledResult<T>} */ ({
            status: "fulfilled",
            value: promiseItem,
          });
        }),
      )
      .then((promiseResults) => {
        for (const promiseResult of promiseResults) {
          res.push(promiseResult);
        }
        defer.resolve();
      });
    return defer.promise;
  }
}

/**
 * @param {ng.IAngularStatic} angular
 * @param {string} [moduleName]
 * @returns {void}
 */
export default function register(angular, moduleName = __AppName__) {
  angular.module(moduleName).decorator("$q", enhanceQ);
}

/**
 * @__PURE__
 * @param {unknown} obj
 * @returns {obj is ng.IPromise<unknown>}
 */
function isNgIPromise(obj) {
  // eslint-disable-next-line no-implicit-coercion
  return !!obj && typeof obj === "object" && "then" in obj;
}

testSuite.$inject = ["$q"];
/**
 * @param {ng.IQEnhanceService} $q
 */
function testSuite($q) {
  const promise1 = $q.resolve(3);
  const promise2 = new $q((resolve, reject) => setTimeout(reject, 100, "foo"));
  const promises = [promise1, promise2];

  $q.allSettled(promises).then((results) => {
    for (const result of results) console.log(result.status);
  });
}
